#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <linux/poll.h>

#define IMX6UIRQ_CNT        1
#define IMX6UIRQ_NAME       "imx6uirq"
#define KEY_NUM             1
#define KEY0VALUE           0x01
#define INVAKEY             0xff


/*key结构体*/
struct irq_keydesc{
    int gpio;
    int irqnum;
    char name[10];
    unsigned char value;        //按键值
    irqreturn_t (*handler)(int, void *);
    struct tasklet_struct tasklet;
};
struct imx6uirq_dev {
        int major;
        int minor;
        dev_t devid;
        struct cdev cdev;
        struct class *class;
        struct device *device;
        struct device_node *node;
        struct timer_list timer;
        struct irq_keydesc irqkey[KEY_NUM];

        atomic_t keyvalue;
        atomic_t keyrelease;
        wait_queue_head_t r_wait;       //等待队列头
};
static struct imx6uirq_dev imx6uirqdev;
static int imx6uirq_open(struct inode *inode,struct file *file){
    file->private_data = &imx6uirqdev;
    return 0;
}
static int imx6uirq_release(struct inode *inode,struct file *file){
    return 0;
}
static ssize_t imx6uirq_read(struct file *file,char __user *buf,size_t cnt,loff_t *ops){
    int ret = 0;
    unsigned char keyvalue;
    unsigned char keyrelease;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)file->private_data;
    if (file->f_flags & O_NONBLOCK) {       //非阻塞模式
        if (atomic_read(&dev->keyrelease) == 0) {       //没有按键按下
            return -EAGAIN;
        }
    }else{
        printk("asdfjkajdfk\r\n");
        wait_event_interruptible(dev->r_wait, atomic_read(&dev->keyrelease));
    }

    keyvalue = atomic_read(&dev->keyvalue);
    keyrelease = atomic_read(&dev->keyrelease);
    if (keyrelease) {
        if (keyvalue & 0x80) {
            keyvalue &= ~0x80;
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        }else{
            goto data_error;
        }
        atomic_set(&dev->keyrelease, 0);

    }
    data_error:
    return ret;
}

static unsigned int imx6uirq_poll(struct file *file, struct poll_table_struct *wait) {
    int mask = 0;
    struct imx6uirq_dev *dev = file->private_data;
    poll_wait(file, &dev->r_wait, wait);
    if (atomic_read(&dev->keyrelease)) {
        mask = POLL_IN | POLLRDBAND;
    }
    return mask;
}
static const struct file_operations imx6uirq_fops = {
        .owner = THIS_MODULE,
        .open = imx6uirq_open,
        .release = imx6uirq_release,
        .read = imx6uirq_read,
        .poll = imx6uirq_poll,
};
static irqreturn_t key0_handler(int irq,void *dev_id)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;
    printk("key_handle is runing\r\n");
    tasklet_schedule(&dev->irqkey[0].tasklet);
    return IRQ_HANDLED;
}
static void tasklet_func(unsigned long data)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)data;
    printk("tasklet func is runing________tasklet\r\n");
    dev->timer.data = data;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
}
static void timer_func(unsigned long data)
{

    int value = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)data;
    value = gpio_get_value(dev->irqkey[0].gpio);        //读取引脚电平状态
    printk("timer_func is runing ...... value = %d\r\n", value);
    if (value == 0) {
        atomic_set(&dev->keyvalue, dev->irqkey[0].value);
    } else if (value == 1) {
        atomic_set(&dev->keyvalue, (dev->irqkey[0].value) | 0x80);
        atomic_set(&dev->keyrelease, 1);        //完成了按键的释放过程.
    }
    if (atomic_read(&dev->keyrelease))
    {
        wake_up(&dev->r_wait);
    }
}
static int keyio_init(struct imx6uirq_dev *dev){
    int ret = 0;
    int i = 0;
    /*1,按键初始化*/
    dev->node = of_find_node_by_path("/key");
    if (dev->node == NULL) {
        ret = -ENAVAIL;
        goto fail_node;
    }
    /*读取gpio编号*/
    for (i = 0; i < KEY_NUM; i++) {
        dev->irqkey[i].gpio = of_get_named_gpio(dev->node, "key-gpios", i);
    }
    /*申请GPIO*/
    for (i = 0; i < KEY_NUM; ++i) {
        memset(dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name));
        sprintf(dev->irqkey[i].name, "KEY%d", i);
        gpio_request(dev->irqkey[i].gpio, dev->irqkey[i].name);
        gpio_direction_input(dev->irqkey[i].gpio);
        dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);
    }
    /*按键中断初始化*/
    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value = KEY0VALUE;
    for (i = 0; i < KEY_NUM; i++) {
        ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                          dev->irqkey[i].name, dev);
        if (ret < 0) {
            printk("irq is %d\r\n", dev->irqkey[i].irqnum);
            goto fail_request_irq;
        }
        tasklet_init(&dev->irqkey[i].tasklet, tasklet_func, (unsigned long) dev);
    }
    /*初始化定时器*/
    init_timer(&dev->timer);
    dev->timer.function  = timer_func;
    return 0;
    fail_request_irq:
    fail_node:
    return ret;
}
static int __init imx6uirq_init(void){
    int ret = 0;
    printk("imx6uirq_init\r\n");
    imx6uirqdev.major = 0;
    if (imx6uirqdev.major) {
        imx6uirqdev.devid = MKDEV(imx6uirqdev.major, 0);
        ret = register_chrdev_region(imx6uirqdev.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
    } else{
        ret = alloc_chrdev_region(&imx6uirqdev.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
    }
    if (ret < 0) {
        goto fail_devid;
    }
    printk("major = %d, minor = %d\r\n", imx6uirqdev.major, imx6uirqdev.minor);
    imx6uirqdev.cdev.owner = THIS_MODULE;
    cdev_init(&imx6uirqdev.cdev, &imx6uirq_fops);
    ret = cdev_add(&imx6uirqdev.cdev, imx6uirqdev.devid, IMX6UIRQ_CNT);
    if (ret < 0) {
        goto fail_cdevadd;
    }

    imx6uirqdev.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirqdev.class)) {
        ret = PTR_ERR(imx6uirqdev.class);
        goto fail_class;
    }
    imx6uirqdev.device = device_create(imx6uirqdev.class, NULL, imx6uirqdev.devid, NULL, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirqdev.device)) {
        ret = PTR_ERR(imx6uirqdev.device);
        goto fail_device;
    }
    ret = keyio_init(&imx6uirqdev);
    if (ret < 0) {
        goto fail_keyinit;
    }

    atomic_set(&imx6uirqdev.keyvalue ,INVAKEY);
    atomic_set(&imx6uirqdev.keyrelease,0);

    init_waitqueue_head(&imx6uirqdev.r_wait);
    return 0;
    fail_keyinit:
    device_destroy(imx6uirqdev.class, imx6uirqdev.devid);
    fail_device:
    class_destroy(imx6uirqdev.class);
    fail_class:
    cdev_del(&imx6uirqdev.cdev);
    fail_cdevadd:
    unregister_chrdev_region(imx6uirqdev.devid, IMX6UIRQ_CNT);
    fail_devid:
    return ret ;
}
static void __exit imx6uirq_exit(void){
    int ret = 0;
    int i= 0;
    for (i = 0; i < KEY_NUM; i++)
    {
        free_irq(imx6uirqdev.irqkey[i].irqnum, &imx6uirqdev);
    }
    for (i = 0; i < KEY_NUM; i++)
    {
        gpio_free(imx6uirqdev.irqkey[i].gpio);
    }
    del_timer_sync(&imx6uirqdev.timer);
    printk("imx6uirq_exit\r\n");
    device_destroy(imx6uirqdev.class, imx6uirqdev.devid);
    class_destroy(imx6uirqdev.class);
    cdev_del(&imx6uirqdev.cdev);
    unregister_chrdev_region(imx6uirqdev.devid, IMX6UIRQ_CNT);

}
module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WXY");